import 'package:flutter/foundation.dart';
import 'dart:ui' as ui;
import 'dart:io';
import '../models/map_models.dart';
import '../services/tmx_parser.dart';

class MapEditorProvider extends ChangeNotifier {
  EditorState _state = EditorState();
  final Map<String, ui.Image> _imageCache = {};
  
  EditorState get state => _state;
  TileMap? get currentMap => _state.currentMap;
  bool get hasMap => _state.currentMap != null;
  Map<String, ui.Image> get imageCache => _imageCache;
  
  // Getters for common state properties
  int get selectedTileId => _state.selectedTileId;
  int get currentLayerIndex => _state.currentLayerIndex;
  EditMode get editMode => _state.editMode;
  bool get showGrid => _state.showGrid;
  bool get showRuler => _state.showRuler;
  double get zoomLevel => _state.zoomLevel;
  MapLayer? get currentLayer {
    if (!hasMap || _state.currentLayerIndex >= currentMap!.layers.length) {
      return null;
    }
    return currentMap!.layers[_state.currentLayerIndex];
  }

  // Create new map
  void createNewMap({
    required int width,
    required int height,
    required int tileWidth,
    required int tileHeight,
  }) {
    final newMap = TileMap.createNew(
      width: width,
      height: height,
      tileWidth: tileWidth,
      tileHeight: tileHeight,
    );
    
    _state = _state.copyWith(
      currentMap: newMap,
      currentFilePath: null,
      selectedTileId: 0,
      currentLayerIndex: 0,
      editMode: EditMode.view,
    );
    
    _clearUndoRedo();
    notifyListeners();
  }

  // Open map from file
  Future<void> openMap(String filePath) async {
    try {
      final map = await TMXParser.parseFromFile(filePath);
      _state = _state.copyWith(
        currentMap: map,
        currentFilePath: filePath,
        currentLayerIndex: 0,
      );
      _clearUndoRedo();
      notifyListeners();
    } catch (e) {
      throw Exception('Failed to open map: $e');
    }
  }

  // Save map
  Future<void> saveMap([String? filePath]) async {
    if (!hasMap) return;
    
    final path = filePath ?? _state.currentFilePath;
    if (path == null) {
      throw Exception('No file path specified');
    }

    try {
      await TMXParser.saveToFile(currentMap!, path);
      _state = _state.copyWith(currentFilePath: path);
      notifyListeners();
    } catch (e) {
      throw Exception('Failed to save map: $e');
    }
  }

  // Edit tile at position
  void setTile(int x, int y, int tileId) {
    if (!hasMap || currentLayer == null) return;
    
    if (x >= 0 && x < currentLayer!.width && y >= 0 && y < currentLayer!.height) {
      _saveToUndoStack();
      
      final newData = List<List<int>>.from(
        currentLayer!.data.map((row) => List<int>.from(row.map((cell) => cell is int ? cell : int.tryParse(cell.toString()) ?? 0)))
      );
      newData[y][x] = tileId;
      
      final newLayer = currentLayer!.copyWith(data: newData);
      final newLayers = List<MapLayer>.from(currentMap!.layers);
      newLayers[_state.currentLayerIndex] = newLayer;
      
      final newMap = TileMap(
        version: currentMap!.version,
        orientation: currentMap!.orientation,
        renderOrder: currentMap!.renderOrder,
        width: currentMap!.width,
        height: currentMap!.height,
        tileWidth: currentMap!.tileWidth,
        tileHeight: currentMap!.tileHeight,
        tileSets: currentMap!.tileSets,
        layers: newLayers,
        nextObjectId: currentMap!.nextObjectId,
        nextLayerId: currentMap!.nextLayerId,
      );
      
      _state = _state.copyWith(currentMap: newMap);
      notifyListeners();
    }
  }

  // Select tile
  void selectTile(int tileId) {
    _state = _state.copyWith(selectedTileId: tileId);
    notifyListeners();
  }

  // Change edit mode
  void setEditMode(EditMode mode) {
    _state = _state.copyWith(editMode: mode);
    notifyListeners();
  }

  // Toggle grid visibility
  void toggleGrid() {
    _state = _state.copyWith(showGrid: !_state.showGrid);
    notifyListeners();
  }

  // Toggle ruler visibility
  void toggleRuler() {
    _state = _state.copyWith(showRuler: !_state.showRuler);
    notifyListeners();
  }

  // Zoom functions
  void zoomIn() {
    final newZoom = (_state.zoomLevel * 1.2).clamp(0.1, 5.0);
    _state = _state.copyWith(zoomLevel: newZoom);
    notifyListeners();
  }

  void zoomOut() {
    final newZoom = (_state.zoomLevel / 1.2).clamp(0.1, 5.0);
    _state = _state.copyWith(zoomLevel: newZoom);
    notifyListeners();
  }

  void setZoom(double zoom) {
    _state = _state.copyWith(zoomLevel: zoom.clamp(0.1, 5.0));
    notifyListeners();
  }

  // Layer management
  void selectLayer(int index) {
    if (!hasMap || index < 0 || index >= currentMap!.layers.length) return;
    
    _state = _state.copyWith(currentLayerIndex: index);
    notifyListeners();
  }

  void addLayer(String name, {bool isObjectLayer = false}) {
    if (!hasMap) return;
    
    final newLayer = MapLayer(
      id: currentMap!.nextLayerId.toString(),
      name: name,
      width: currentMap!.width,
      height: currentMap!.height,
      data: List.generate(
        currentMap!.height,
        (y) => List.generate(currentMap!.width, (x) => 0),
      ),
      isObjectLayer: isObjectLayer,
    );

    final newLayers = List<MapLayer>.from(currentMap!.layers)..add(newLayer);
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId + 1,
    );

    _state = _state.copyWith(
      currentMap: newMap,
      currentLayerIndex: newLayers.length - 1,
    );
    notifyListeners();
  }

  void deleteCurrentLayer() {
    if (!hasMap || currentMap!.layers.length <= 1) return;
    
    final newLayers = List<MapLayer>.from(currentMap!.layers);
    newLayers.removeAt(_state.currentLayerIndex);
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );

    final newIndex = (_state.currentLayerIndex).clamp(0, newLayers.length - 1);
    
    _state = _state.copyWith(
      currentMap: newMap,
      currentLayerIndex: newIndex,
    );
    notifyListeners();
  }

  void renameLayer(int layerIndex, String newName) {
    if (!hasMap || layerIndex < 0 || layerIndex >= currentMap!.layers.length) return;
    
    final targetLayer = currentMap!.layers[layerIndex];
    final newLayer = targetLayer.copyWith(name: newName);
    final newLayers = List<MapLayer>.from(currentMap!.layers);
    newLayers[layerIndex] = newLayer;
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(currentMap: newMap);
    notifyListeners();
  }

  void renameCurrentLayer(String newName) {
    if (!hasMap || currentLayer == null) return;
    
    final newLayer = currentLayer!.copyWith(name: newName);
    final newLayers = List<MapLayer>.from(currentMap!.layers);
    newLayers[_state.currentLayerIndex] = newLayer;
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(currentMap: newMap);
    notifyListeners();
  }

  void toggleLayerVisibility() {
    if (!hasMap || currentLayer == null) return;
    
    final newLayer = currentLayer!.copyWith(visible: !currentLayer!.visible);
    final newLayers = List<MapLayer>.from(currentMap!.layers);
    newLayers[_state.currentLayerIndex] = newLayer;
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(currentMap: newMap);
    notifyListeners();
  }

  // TileSet management
  void addTileSet(TileSet tileSet) {
    if (!hasMap) return;
    
    final newTileSets = List<TileSet>.from(currentMap!.tileSets)..add(tileSet);
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: newTileSets,
      layers: currentMap!.layers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(currentMap: newMap);
    
    // 预加载图块集图片
    loadTileSetImage(tileSet.imagePath);
    
    notifyListeners();
  }

  // Undo/Redo functionality
  void _saveToUndoStack() {
    if (!hasMap || currentLayer == null) return;
    
    final currentData = currentLayer!.data;
    final newUndoStack = List<List<List<int>>>.from(_state.undoStack);
    newUndoStack.add(List.from(currentData.map((row) => List<int>.from(row.map((cell) => cell is int ? cell : int.tryParse(cell.toString()) ?? 0)))));
    
    // Limit undo stack size
    if (newUndoStack.length > 50) {
      newUndoStack.removeAt(0);
    }
    
    _state = _state.copyWith(
      undoStack: newUndoStack,
      redoStack: [], // Clear redo stack when new action is performed
    );
  }

  void undo() {
    if (!hasMap || currentLayer == null || _state.undoStack.isEmpty) return;
    
    final newUndoStack = List<List<List<int>>>.from(_state.undoStack);
    final newRedoStack = List<List<List<int>>>.from(_state.redoStack);
    
    // Save current state to redo stack
    newRedoStack.add(List.from(currentLayer!.data.map((row) => List<int>.from(row.map((cell) => cell is int ? cell : int.tryParse(cell.toString()) ?? 0)))));
    
    // Restore previous state
    final previousData = newUndoStack.removeLast();
    
    final newLayer = currentLayer!.copyWith(data: previousData);
    final newLayers = List<MapLayer>.from(currentMap!.layers);
    newLayers[_state.currentLayerIndex] = newLayer;
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(
      currentMap: newMap,
      undoStack: newUndoStack,
      redoStack: newRedoStack,
    );
    notifyListeners();
  }

  void redo() {
    if (!hasMap || currentLayer == null || _state.redoStack.isEmpty) return;
    
    final newUndoStack = List<List<List<int>>>.from(_state.undoStack);
    final newRedoStack = List<List<List<int>>>.from(_state.redoStack);
    
    // Save current state to undo stack
    newUndoStack.add(List.from(currentLayer!.data.map((row) => List<int>.from(row.map((cell) => cell is int ? cell : int.tryParse(cell.toString()) ?? 0)))));
    
    // Restore next state
    final nextData = newRedoStack.removeLast();
    
    final newLayer = currentLayer!.copyWith(data: nextData);
    final newLayers = List<MapLayer>.from(currentMap!.layers);
    newLayers[_state.currentLayerIndex] = newLayer;
    
    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: currentMap!.width,
      height: currentMap!.height,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(
      currentMap: newMap,
      undoStack: newUndoStack,
      redoStack: newRedoStack,
    );
    notifyListeners();
  }

  void _clearUndoRedo() {
    _state = _state.copyWith(
      undoStack: [],
      redoStack: [],
    );
  }

  bool get canUndo => _state.undoStack.isNotEmpty;
  bool get canRedo => _state.redoStack.isNotEmpty;

  // Resize map
  void resizeMap(int newWidth, int newHeight) {
    if (!hasMap) return;
    
    final newLayers = currentMap!.layers.map((layer) {
      final newData = <List<int>>[];
      
      for (int y = 0; y < newHeight; y++) {
        final newRow = <int>[];
        for (int x = 0; x < newWidth; x++) {
          if (y < layer.height && x < layer.width) {
            // Copy existing data
            newRow.add(layer.data[y][x]);
          } else {
            // Fill with empty tiles
            newRow.add(0);
          }
        }
        newData.add(newRow);
      }
      
      return layer.copyWith(
        width: newWidth,
        height: newHeight,
        data: newData,
      );
    }).toList();

    final newMap = TileMap(
      version: currentMap!.version,
      orientation: currentMap!.orientation,
      renderOrder: currentMap!.renderOrder,
      width: newWidth,
      height: newHeight,
      tileWidth: currentMap!.tileWidth,
      tileHeight: currentMap!.tileHeight,
      tileSets: currentMap!.tileSets,
      layers: newLayers,
      nextObjectId: currentMap!.nextObjectId,
      nextLayerId: currentMap!.nextLayerId,
    );
    
    _state = _state.copyWith(currentMap: newMap);
    _clearUndoRedo();
    notifyListeners();
  }

  // Image cache management
  Future<void> loadTileSetImage(String imagePath) async {
    if (_imageCache.containsKey(imagePath)) return;
    
    try {
      final file = File(imagePath);
      if (!file.existsSync()) {
        print('Image file does not exist: $imagePath');
        return;
      }
      
      final bytes = await file.readAsBytes();
      final codec = await ui.instantiateImageCodec(bytes);
      final frame = await codec.getNextFrame();
      _imageCache[imagePath] = frame.image;
      
      print('Loaded image into cache: $imagePath');
      notifyListeners();
    } catch (e) {
      print('Failed to load image: $imagePath, error: $e');
    }
  }

  void clearImageCache() {
    _imageCache.clear();
  }
}